import socket
import struct
import time
import threading

# 组播配置
MULTICAST_GROUP = '224.1.1.1'
MULTICAST_PORT = 5007
BIND_ADDRESS = '0.0.0.0'
TTL = 2  # 生存时间，控制组播范围


def sender():
    """组播发送方"""
    try:
        # 创建UDP socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        # 设置TTL
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, TTL)

        print(f"发送方准备就绪，将向组播组 {MULTICAST_GROUP}:{MULTICAST_PORT} 发送数据...")

        message_count = 0
        while True:
            message = f"组播测试消息 {message_count}"
            sock.sendto(message.encode(), (MULTICAST_GROUP, MULTICAST_PORT))
            print(f"已发送: {message}")
            message_count += 1
            time.sleep(3)

    except KeyboardInterrupt:
        print("\n发送方停止")
    finally:
        sock.close()


def receiver(name="接收方1"):
    """组播接收方"""
    try:
        # 创建UDP socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        # 绑定端口
        sock.bind((BIND_ADDRESS, MULTICAST_PORT))

        # 加入组播组
        group = socket.inet_aton(MULTICAST_GROUP)
        mreq = struct.pack('4sL', group, socket.INADDR_ANY)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

        print(f"{name} 已加入组播组 {MULTICAST_GROUP}:{MULTICAST_PORT}，等待接收数据...")

        while True:
            data, addr = sock.recvfrom(1024)
            print(f"{name} 收到来自 {addr} 的消息: {data.decode()}")

    except KeyboardInterrupt:
        print(f"\n{name} 停止接收")
    finally:
        # 离开组播组
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_DROP_MEMBERSHIP, mreq)
        sock.close()
        print(f"{name} 已离开组播组")


def test_join_leave():
    """测试加入和离开组播组"""
    print("\n=== 测试1: 组播组成员管理 ===")
    print("1. 验证主机能否成功加入组播组")
    recv_thread = threading.Thread(target=receiver, args=("测试接收方",))
    recv_thread.start()
    time.sleep(5)  # 等待加入组播组

    print("\n2. 验证主机能否正常离开组播组")
    recv_thread.join()
    print("测试接收方已停止")


def test_multiple_receivers():
    """测试多个接收方"""
    print("\n=== 测试2: 组成员动态变化 ===")
    print("启动3个接收方模拟组成员动态变化")

    receivers = []
    for i in range(1, 4):
        t = threading.Thread(target=receiver, args=(f"接收方{i}",))
        t.start()
        receivers.append(t)
        time.sleep(2)  # 间隔启动

    time.sleep(10)

    print("\n模拟组成员离开...")
    for t in receivers:
        t.join()
    print("所有接收方已停止")


def test_non_member():
    """测试非组成员接收情况"""
    print("\n=== 测试3: 非组成员测试 ===")
    print("启动一个不加入组播组的接收方")

    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((BIND_ADDRESS, MULTICAST_PORT))

        print("非组成员接收方准备就绪 (未加入组播组)")
        sock.settimeout(5)  # 设置超时避免无限等待
        data, addr = sock.recvfrom(1024)
        print(f"非组成员意外收到消息: {data.decode()}")
    except socket.timeout:
        print("非组成员未收到任何消息 (符合预期)")
    finally:
        sock.close()


if __name__ == "__main__":
    # 启动发送方线程
    sender_thread = threading.Thread(target=sender)
    sender_thread.start()

    time.sleep(1)  # 确保发送方先启动

    # 执行测试用例
    test_join_leave()
    test_multiple_receivers()
    test_non_member()

    # 停止发送方
    sender_thread.join()
